package com.gitee.qdbp.base.utils;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.regex.Pattern;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.subject.WebSubject;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.support.RequestContext;
import org.springframework.web.servlet.support.RequestContextUtils;
import com.gitee.qdbp.tools.files.PathTools;
import com.gitee.qdbp.tools.utils.ConvertTools;
import com.gitee.qdbp.tools.utils.StringTools;

/**
 * Web工具类<br>
 * 一般情况使用WebTools.me.getXxx()方法即可<br>
 * 但在404或500页面, 应使用new WebTools(request), 因为通过RequestContextHolder.getRequestAttributes()获取Request为空<br>
 *
 * @author zhaohuihua
 * @version 140403
 */
public class WebTools {

    private final HttpServletRequest request;

    /** 实例 **/
    public static final WebTools me = new WebTools();

    private WebTools() {
        this.request = null;
    }

    /** 构造函数 */
    public WebTools(HttpServletRequest request) {
        this.request = request;
    }

    /**
     * 返回当前请求对象
     *
     * @return 当前请求对象
     */
    public HttpServletRequest getRequest() {
        if (request != null) {
            return request;
        }
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        if (ra != null) {
            return ((ServletRequestAttributes) ra).getRequest();
        } else {
            try {
                Subject subject = SecurityUtils.getSubject();
                if (subject instanceof WebSubject) {
                    ServletRequest sr = ((WebSubject) subject).getServletRequest();
                    if (sr instanceof HttpServletRequest) {
                        return (HttpServletRequest) sr;
                    }
                }
            } catch (Exception ignore) {
            }
            return null;
        }
    }

    /**
     * 获取Web工程容器路径<br>
     * 如果部署为ROOT将返回空字符串, 否则返回斜杠加工程名; 如工程名为site, 返回/site
     *
     * @return context path
     */
    public String getContextPath() {
        HttpServletRequest request = getRequest();
        return request == null ? null : request.getContextPath();
    }

    /**
     * 获取应用根目录
     *
     * @return root path
     */
    public String getWebRoot() {
        HttpServletRequest request = getRequest();
        return request == null ? null : request.getServletContext().getRealPath("/");
    }

    /**
     * 获取相对于根目录的路径
     *
     * @param path 相对路径
     * @return root path
     */
    public String getWebRoot(String path) {
        HttpServletRequest request = getRequest();
        return request == null ? null : request.getServletContext().getRealPath(path);
    }

    /**
     * 获取相对于根路径的URL
     *
     * @return 绝对路径URL
     */
    public String getWebUrl() {
        // 获取服务器请求信息
        HttpServletRequest request = getRequest();
        String scheme = request.getScheme();
        String host = request.getServerName();
        int port = request.getServerPort();
        String ctxPath = request.getContextPath();
        String prefix = scheme + "://" + host;
        if (scheme == "http" && port != 80 || scheme == "https" && port != 443) {
            prefix += ":" + port;
        }
        return PathTools.concat(prefix, ctxPath);
    }

    /**
     * 获取相对于根路径的URL
     *
     * @param uri 相对路径, 如果uri以/开头则不追加ContextPath
     * @return 绝对路径URL
     */
    public String getWebUrl(String uri) {
        if (uri == null || StringTools.isUrl(uri)) {
            return uri;
        }
        // 获取服务器请求信息
        HttpServletRequest request = getRequest();
        String scheme = request.getScheme();
        String host = request.getServerName();
        int port = request.getServerPort();
        String prefix = scheme + "://" + host;
        if (scheme == "http" && port != 80 || scheme == "https" && port != 443) {
            prefix += ":" + port;
        }
        if (uri.startsWith("/")) {
            return PathTools.concat(prefix, uri);
        } else {
            String ctxPath = request.getContextPath();
            return PathTools.concat(prefix, ctxPath, uri);
        }
    }

    /**
     * 根据服务器请求信息替换URL中的占位符<br>
     * 如: getServerURL("http://{ServerName}:{ServerPort}/{ContextPath}/")<br>
     * 返回 http://127.0.0.1:8080/xxx/
     *
     * @author zhaohuihua
     * @param url 替换前的URL
     * @return 替换后的URL
     */
    public String getServerURL(String url) {
        if (url == null) {
            return url;
        }
        url = url.trim();
        if (url.indexOf("{") < 0 || url.indexOf("}") < 0) {
            return url;
        }
        // 获取服务器请求信息
        HttpServletRequest request = getRequest();
        String scheme = request.getScheme();
        String host = request.getServerName();
        String port = String.valueOf(request.getServerPort());
        // ContextPath是以/斜杠开头的, substring(1)去掉斜杠
        String ctx = request.getContextPath();
        if (ctx != null && ctx.length() > 0) {
            ctx = request.getContextPath().substring(1);
        }
        // 占位符正则表达式
        Pattern pScheme = Pattern.compile("\\{[Ss]cheme\\}");
        Pattern pHost = Pattern.compile("\\{[Ss]erverName\\}");
        Pattern pPort = Pattern.compile("\\{[Ss]erverPort\\}");
        Pattern pCtx = Pattern.compile("\\{[Cc]ontextPath\\}");
        // 替换占位符
        url = pScheme.matcher(url).replaceFirst(scheme);
        url = pHost.matcher(url).replaceFirst(host);
        url = pPort.matcher(url).replaceFirst(port);
        url = pCtx.matcher(url).replaceFirst(ctx);
        return Pattern.compile("(?<!:)//").matcher(url).replaceAll("/");
    }

    /** 获取IP地址 **/
    public String getIpAddress() {
        HttpServletRequest request = getRequest();
        if (request == null) {
            return null;
        }
        // request.getHeader(key) 不区分大小写
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Read-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("http_client_ip");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // X-Forwarded-For: client, proxy1, proxy2
        if (ip != null && ip.indexOf(",") != -1) {
            ip = ip.substring(0, ip.indexOf(",")).trim();
            if ("unknown".equalsIgnoreCase(ip)) {
                ip = ip.substring(ip.lastIndexOf(",") + 1, ip.length()).trim();
            }
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    /** 获取请求参数 **/
    public Map<String, String> getRequestParams() {
        HttpServletRequest request = getRequest();
        if (request == null) {
            return null;
        }

        Map<String, String> params = new HashMap<>();
        for (Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            if (values == null || values.length == 0) {
                params.put(name, null);
            } else if (values.length == 1) {
                params.put(name, values[0]);
            } else {
                StringBuilder buffer = new StringBuilder();
                for (String value : values) {
                    if (buffer.length() > 0) {
                        buffer.append(",");
                    }
                    buffer.append(value);
                }
                params.put(name, buffer.toString());
            }
        }
        return params;
    }

    /** 获取Spring的应用上下文环境(WebApplicationContext) **/
    public WebApplicationContext getApplicationContext() {
        HttpServletRequest request = getRequest();
        if (request == null) return null;

        // @see RequestContext.initContext()
        String wacKey = RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE;
        WebApplicationContext wac = (WebApplicationContext) request.getAttribute(wacKey);
        if (wac == null) {
            wac = RequestContextUtils.findWebApplicationContext(request, request.getServletContext());
            if (wac != null) {
                return wac;
            }
        }
        return null;
    }

    /** 获取Spring的应用上下文环境中的Properties实例 **/
    public Properties getContextProperties() {
        ApplicationContext ac = getApplicationContext();
        // WebApplicationContext中只存在一个名称为systemProperties的Properties
        // WebApplicationContext.getParent()能找到setting,systemProperties两个Properties
        if (ac == null) return null;
        if (ac.getParent() != null) {
            ac = ac.getParent();
        }
        Map<String, Properties> map = ac.getBeansOfType(Properties.class);
        map.remove("systemProperties");

        // 去掉systemProperties之后如果只剩下一个就取这个
        // 剩下不只一个就优先取setting, 如果没有就报错
        if (map.isEmpty()) {
            return null;
        } else if (map.size() == 1) {
            return map.values().iterator().next();
        } else {
            if (map.containsKey("setting")) {
                return map.get("setting");
            } else if (map.containsKey("settings")) {
                return map.get("settings");
            } else {
                String keys = ConvertTools.joinToString(map.keySet(), ",");
                throw new IllegalStateException("ContextPropertiesMultipleInstancesFound: " + keys);
            }
        }
    }

}
